package com.homework;

/**
 * @Author 小闽
 * @Date 2024/9/22 11:24
 * @PackageName: com.homework
 * @ClassName: HomeWork06
 * @Description: 练习6
 * @Version 1.0
 */
public class HomeWork06 {
    public static void main(String[] args) {
        /**
         * 1. 有一个交通工具接口类Vehicles,有work接口
         * 2. 有Horse类和Boat类分别实现Vehicles
         * 3. 创建交通工具工厂类，有两个方法分别获得交通工具Horse和Boat
         * 4. 有Person类，有name和Vehicles属性，在构造器中为两个属性赋值
         * 5. 实例化Person对象"唐僧"，要求一般情况下用Horse作为交通工具，遇到大河时用Boat作为交通工具
         * 6. 增加一个情况，如果唐僧要过火焰山，使用飞机 ==> 涉及到程序的扩展性问题，
         */
        // 然后再判断这个人是否为一般情况，就是会不会遇到大河
        Person p1 = new Person("唐僧", null);
        p1.common();
        p1.river();
        p1.common();
        p1.common();
        p1.river();
        p1.fireMon();
        p1.river();
        p1.common();
        p1.fireMon();
        p1.fireMon();
    }
}

// 实现一个交通工具的接口
interface Vehicles{
    // 交通工具工作的抽象方法
    public void work();
}
// Horse类
class Horse implements Vehicles{

    @Override
    public void work() {
        System.out.println("一般情况下使用马儿前进...");
    }
}
// Boat类
class Boat implements Vehicles{

    @Override
    public void work() {
        System.out.println("过河时使用小船前进...");
    }
}
// 火焰山类
class Plane implements Vehicles{

    @Override
    public void work() {
        System.out.println("过火焰山使用飞机前进...");
    }
}
// 问题： 但是根据上述编程思想去实现虽然可以，却突出一个重要的问题，就是对象创建成功后，传入了两个参数，
// 一个是name,一个是vehicles，而在调用过河和一般情况下时会直接去创建对应的交通工具的对象
// 这样就会浪费掉Person创建对象中的参数，与题指违背。
class Person{
    private String name;
    private Vehicles vehicles;
    // 在创建人对象的时候，会事先给他分配一个交通工具
    public Person(String name, Vehicles vehicles) {
        this.name = name;
        this.vehicles = vehicles;
    }
     // 实例化Person对象"唐僧"，要求一般情况下用Horse作为交通工具，遇到大河时用Boat作为交通工具
    // 这就是面向对象编程思想，即把具体的要求，封装成一个方法——>这就是编程思想
    // 这是遇到大河的情况
    public void river(){
        // 先创建出一艘船,即实例化交通工具工厂类,先把船造出来
        // 得到小船
        // 在这里可以这样去解决，先去判断它这个传入的vehicles是否为null,如果为空就去创建对象
        // 如果不等于null,说明vehicles赋值，则直接调用
        // 如果 vehicles是 null, vehicles instanceof Boat => false
        // 如果 vehicles是 Horse，vehicles instanceof Boat => false
        // 如果 vehicles是 Boat, vehicles instanceof Boat => true


        // 为什么要使用 !(vehicles instanceof Boat) 这种思想，
        // 因为当传入的 Person 类对象中的接口参数是new Horse时候，这个时候如果直接去使用 vehicles == null这种方式
        // 虽然能够解决 vehicles 设置值浪费的问题，但是当去调用
        // p1.common();
        // p1.river();
        //时就会发生只会输出一般情况下使用马儿前进...
        // 这是因为传入的接口参数是 new Horse，所以vehicles的值永远是马儿对象，也就是并没有去判断
        // vehicles的当前对象类型是否是Boat或者是Horse，因此需要用到这种方式去解决
        if (!(vehicles instanceof Boat)){
            vehicles = TransportationSharedFactory.getBoat();
        }
        vehicles.work();
    }
    // 这是一般的情况
    public void common(){
        // 得到马儿
        // 这里也是一样
        if (!(vehicles instanceof Horse)){
            // 在这里实现了向上转型，vehicles是接口，而Horse和Boat是分别实现了接口的类对象
            // 即将子类对象赋值给实现子类对象的接口或继承父类对象的引用
            vehicles = TransportationSharedFactory.getHorse();
        }
        // 在这里体现了接口的调用，并且也体现了多态的特性，调用vehicles接口中的wrok方法
        // 具体是调用哪个方法根据传入的vehicles的值去调用实现该类的方法
        vehicles.work();
    }
    // 这里是过火焰山的情况
    public void fireMon(){
        if (!(vehicles instanceof Plane)){
            vehicles = TransportationSharedFactory.getPlane();
        }
        vehicles.work();
    }
}
// 交通工具共厂类
class TransportationSharedFactory{
    // 在这里因为唐僧去取经用的是一匹马，并且马儿不会一直换，所以这里采用饿汉式的单例设计模式
    private static Horse horse = new Horse();
    // 同时为了防止直接实例化工厂类，将构造方法进行私有化
    private TransportationSharedFactory(){};
    // 获得Horse交通工具的方法
    public static Horse getHorse(){
//        return new Horse();
        return horse;
    }
    // 获得Boat交通工具的方法
    public static Boat getBoat(){
        return new Boat();
    }
    // 获得飞机交通工具的方法
    public static Plane getPlane(){
        return new Plane();
    }
}